/**
 * @author spider
 * Exposes the manager Methods to the other packages,
 * - Protocol
 *    . All graphic windows must comunicate with other machines using this class.
 *    . No other class should generate "request" messages unless this one.
 *    . Other classes may only generate "reply" messages to messages generated here.
 * - Synchronization
 *    . DeadLock Problems must be solved here.
 *    . If something must be blocked, it should be this thread, not the graphic windows
 * ps: SyncManager and TrafficManager must be protected! :D
 */

package br.unb.core.manager;

import br.unb.core.traffic.Traffic;
import java.util.HashMap;

import br.unb.core.synchronism.Synchronism;
import br.unb.utils.Console;
import br.unb.utils.ConsoleInterface;
import javax.swing.text.DefaultStyledDocument;

public class Manager implements Runnable, ConsoleInterface {

    /** Message Controller, sends message between machines (Created here to assure it is the first initialized)*/
    private static final SyncManager SYNC_MANAGER = SyncManager.getInstance();
    private static final TrafficManager TRAFFIC_MANAGER = TrafficManager.getInstance();
    /*Instance*/
    private static final Manager INSTANCE = new Manager();
    private Thread managerThread = null;

    private Manager() {
        managerThread = (new Thread(this, "Manager"));
        managerThread.start();
    }

    public static Manager getInstance() {
        return INSTANCE;
    }
    static boolean sending = false; //Lock shared between threads
    //Parameters used
    private Traffic traffic = null;
    private String key = null;
    private boolean isRealTime = false;
    private Synchronism syncModel = null;
    private Synchronism resulthandleSync = null;
    private Synchronism resultstopSync = null;
    private Synchronism resultresync = null;
    //private Synchronism  resultresync= null;
    //Request types
    static final int none = 0;//...
    static final int newTraffic = 1;//newTraffic(traffic)
    static final int playGenericTraffic = 2;//playGenericTraffic(key, isRealTime)
    static final int playAll = 3;//playAll(isRealTime)
    static final int stopTraffic = 4;//stopTraffic(key)
    static final int stopAllTraffic = 5;//stopAllTraffic()
    static final int handleSync = 6;//handleSync(syncModel)
    static final int stopSync = 7;//stopSync(syncModel)
    static final int reSync = 8;//reSync(syncModel)
    static Integer request = new Integer(none);

    @Override
    public void run() {
        //Thread that sends control messages
        while (true) {
            switch (request) {
                /***************** Traffic Manager ***********************/
                case none:
                    //managerThread.sleep(1000);
                    break;
                case newTraffic: {
                    TRAFFIC_MANAGER.newTraffic(traffic);
                    synchronized (this) {
                        traffic = null;
                        request = none;
                        sending = false;
                    }
                    println("newTraffic ok");
                }
                break;

                case playGenericTraffic: {
                    TRAFFIC_MANAGER.playGenericTraffic(key, isRealTime);
                    key = null;
                    synchronized (this) {
                        isRealTime = false;
                        request = none;
                        sending = false;
                    }
                    println("PlayGenericTraffic ok");
                }
                break;

                case playAll: {
                    TRAFFIC_MANAGER.playAll(isRealTime);
                    synchronized (this) {
                        isRealTime = false;
                        request = none;
                        sending = false;
                    }
                    println("PlayAll ok");
                }
                break;

                case stopTraffic: {
                    TRAFFIC_MANAGER.stopTraffic(key);
                    synchronized (this) {
                        key = null;
                        request = none;
                        sending = false;
                    }
                    println("StopTraffic ok");
                }
                break;

                case stopAllTraffic: {
                    TRAFFIC_MANAGER.stopAllTraffic();
                    synchronized (this) {
                        request = none;
                        sending = false;
                    }
                    println("StopAllTraffic ok");
                }
                break;

                case handleSync: {
                    resulthandleSync = SYNC_MANAGER.handleSync(syncModel);
                    synchronized (this) {
                        syncModel = null;
                        request = none;
                        sending = false;
                    }
                    println("handleSync ok");
                }
                break;

                case stopSync: {
                    resultstopSync = SYNC_MANAGER.stopSync(syncModel);
                    synchronized (this) {
                        syncModel = null;
                        request = none;
                        sending = false;
                    }
                    println("stopSync ok");
                }
                break;

                case reSync: {
                    resultresync = SYNC_MANAGER.reSync(syncModel);
                    synchronized (this) {
                        syncModel = null;
                        request = none;
                        sending = false;
                    }
                    println("reSync ok");
                }
                break;
                default: {
                    synchronized (this) {
                        request = none;
                        sending = false;
                    }
                }
                break;
            }
        }
    }

    public static HashMap getTrafficMap() {
        return TrafficManager.getInstance().getTrafficMap();
    }

    /***************** Traffic Manager ***********************/
    /**
     * (Executed on Controller machine)
     * Sends a control message to the network the first time a traffic is run to
     * setup variables on the sender and the receiver.
     * @param traffic traffic to be initiated on the target machines
     */
    public void newTraffic(Traffic traffic) {
        //TRAFFIC_MANAGER.newTraffic(traffic);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            synchronized (this) {
                request = newTraffic;
                this.traffic = traffic;
                sending = true;
            }
        }
    }

    /**
     * (Executed on Controller machine)
     * Sends a control message to the network to start the sender and receiver machines
     * @param traffic traffic to be initiated on the target machines
     */
    public void playGenericTraffic(String key, boolean isRealTime) {
        //TRAFFIC_MANAGER.playGenericTraffic(key, isRealTime);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            synchronized (this) {
                request = playGenericTraffic;
                this.key = key;
                this.isRealTime = isRealTime;
                sending = true;
            }
        }
    }

    /**
     * (Executed on Controller machine)
     * Sends a control message to start all traffics on all machines
     */
    public void playAll(boolean isRealTime) {
        //TRAFFIC_MANAGER.playAll(isRealTime);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            synchronized (this) {
                request = playAll;
                this.isRealTime = isRealTime;
                sending = true;
            }
        }
    }

    /**
     * (Executed on controller machine)
     * Sends a control message to the network to stop the traffic on
     * the sender and receiver machines.
     * @param traffic traffic to be removed
     */
    public void stopTraffic(String key) {
        //TRAFFIC_MANAGER.stopTraffic(key);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            synchronized (this) {
                request = stopTraffic;
                this.key = key;
                sending = true;
            }
        }
    }

    /**
     * (Executed on controller machine)
     * Stop all traffics on all machines
     */
    public void stopAllTraffic() {
        //TRAFFIC_MANAGER.stopAllTraffic();
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            synchronized (this) {
                request = stopAllTraffic;
                sending = true;
            }
        }
    }

    /************************* Synchronism ****************************/
    /**
     * (Executed on Manager)
     * Sends a request to the server first and then one to the client to
     * begin the synchronization
     *
     */
    public void handleSync(Synchronism syncModel) {
        //return SYNC_MANAGER.handleSync(syncModel);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            synchronized (this) {
                request = handleSync;
                this.syncModel = syncModel;
                sending = true;
            }
        }
    }

    public Synchronism getHandleSync() {
        Synchronism tmp = resulthandleSync;
        resulthandleSync = null;
        return tmp;
    }

    /**
     * (Executed on Manager (controller machine))
     * Sends one request to the server and one to the client to stop
     * the synchronization
     */
    public synchronized void stopSync(Synchronism syncModel) {
        //return SYNC_MANAGER.stopSync(syncModel);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            request = stopSync;
            this.syncModel = syncModel;
            sending = true;
        }
    }

    public synchronized Synchronism getstopSync() {
        Synchronism tmp = resultstopSync;
        resultstopSync = null;
        return tmp;
    }

    /**
     * (Executed on Manager (controller machine))
     * Sends a request to the server to resynchronize
     */
    public synchronized void reSync(Synchronism syncModel) {
        //return SYNC_MANAGER.reSync(syncModel);
        if (sending) {
            printError("manager busy");
            printError("Operation:" + Integer.toString(request));
        } else {
            request = reSync;
            this.syncModel = syncModel;
            sending = true;
        }
    }

    public synchronized Synchronism getReSync() {
        Synchronism tmp = resultresync;
        resultresync = null;
        return tmp;
    }
    /**Console interface methods*/
    public static DefaultStyledDocument doc = null;

    @Override
    public void printError(String s) {
        Console.println(s, System.err, doc);
    }

    @Override
    public void println(String s) {
        Console.println(s, System.out, doc);
    }

    @Override
    public void setDocument(DefaultStyledDocument doc) {
        Manager.doc = doc;
    }
}
